package com.winit.openapi.interceptor;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.RequestContextUtils;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.winit.common.spi.context.CommandContext;
import com.winit.openapi.apiadapter.controller.util.RequestMsg3EUtil;
import com.winit.openapi.constants.ApiConstant;
import com.winit.openapi.constants.ErrorCode;
import com.winit.openapi.exception.ApiException;
import com.winit.openapi.interceptor.validator.APIRequestHandlerHolder;
import com.winit.openapi.model.RequestMsg;
import com.winit.openapi.util.APISysConfigUtil;
import com.winit.openapi.util.ParameterAdapterUtils;
import com.winit.openapi.util.RequestUtil;
import com.winit.tracing.client.Tracer;
import com.winit.tracing.common.constants.SpanCategory;

/**
 * 数据解析拦截器
 * 
 * @version Revision History
 * 
 * <pre>
 * Author     Version       Date        Changes
 * kang.wang    1.0           2014-10-24     Created
 * 
 * </pre>
 * @since 1.
 */
public class DataParseInterceptor implements HandlerInterceptor {

    private static final Logger     logger        = LoggerFactory.getLogger(DataParseInterceptor.class);

    private static final String     BLANK_WHITE   = "|";

    @Resource
    private APIRequestHandlerHolder apiRequestHandlerHolder;

    @Resource
    private APISysConfigUtil        apiSysConfigUtil;

    // 不需要检查app_key的接口名称集合
    private static List<String>     no_Check_list = new ArrayList<String>();

    static {
        no_Check_list.add("user.getToken");
        no_Check_list.add("getToken");
        no_Check_list.add("tracking.getTracking");
        no_Check_list.add("baseData.countryList");
        no_Check_list.add("winitBaseData.getRegionList");
        no_Check_list.add("winitBaseData.getCityList");
        no_Check_list.add("customer.register");
        no_Check_list.add("customer.activeCustomer");

        // 新注册流程
        no_Check_list.add("ums.temporarily.mailRegister");
        no_Check_list.add("ums.temporarily.activeCustomer");
        no_Check_list.add("ums.temporarily.saveTemporarily");
        no_Check_list.add("ums.temporarily.newUserRegister");
        no_Check_list.add("ums.temporarily.queryRegisterTemporary");
        no_Check_list.add("ums.temporarily.checkIdentityCardUnique");
        no_Check_list.add("ums.temporarily.checkCustomerNameUnique");
        no_Check_list.add("ums.temporarily.queryPromoteUrl");
        no_Check_list.add("ums.temporarily.addPotentialCustomer");
        no_Check_list.add("ums.temporarily.updateClickAccout");
        no_Check_list.add("ums.temporarily.existsRecommendUser");

        no_Check_list.add("ups.addressdata.province");
        no_Check_list.add("ups.addressdata.city");
        no_Check_list.add("ups.addressdata.district");
        no_Check_list.add("ups.addressdata.allCountry");

        no_Check_list.add("ups2.address.allCountry");
        no_Check_list.add("ups2.address.province");
        no_Check_list.add("ups2.address.city");
        no_Check_list.add("ups2.address.district");

        no_Check_list.add("customer.validateLoginName");
        no_Check_list.add("customer.modifyPassword");
        no_Check_list.add("customer.existCustomerByCode");

        no_Check_list.add("winitBaseData.getDistrictList");
        no_Check_list.add("ispTrack.createISPOrderAscan");
        no_Check_list.add("ispTrack.createISPOrderAscanWarehouse");
        no_Check_list.add("customer.activeModifyUserName");
        no_Check_list.add("wh.reservationSendWh.reservationOrderList");
        no_Check_list.add("wh.reservationSendWh.reservationOrderDetail");
        no_Check_list.add("wh.reservationSendWh.waitingReservationList");
        no_Check_list.add("wh.reservationSendWh.editReservationOrder");
        no_Check_list.add("wh.reservationSendWh.createReservationOrder");
        no_Check_list.add("wh.reservationSendWh.printReservationOrder");
        no_Check_list.add("ups.warehouse.queryWarehouseByCode");
        no_Check_list.add("tms.sendWarehouseRule.API");
        no_Check_list.add("user.applyForChangePassword");
        no_Check_list.add("user.modifyUserPasswordByVerificationCode");
        no_Check_list.add("KuaiDi100Callback");
        no_Check_list.add("ums.distributor.registerDistributor");

        // 金融
        no_Check_list.add("ums.financeUser.registerFinanceUser");
        no_Check_list.add("ums.financeUser.getFinanceUserToken");
        no_Check_list.add("ums.financeUser.sendVerificationCode");
        no_Check_list.add("ums.financeUser.checkAccountIsExist");
        no_Check_list.add("ums.financeUser.checkVerificationCode");
        no_Check_list.add("ums.financeUser.forgetPassword");
        no_Check_list.add("ums.financeUser.loginForFinanceUser");

        // 翼支付异步回调
        no_Check_list.add("sms.onLineRecharge.bpepNotice");

        // Payoneer支付IPCN
        no_Check_list.add("sms.onLineRecharge.payoneerPayIpcn");

        // 易联异步回调确认充值成功
        no_Check_list.add("sms.onLineRecharge.confirmRechargeSuccess");

        // PayPal异步回调确认充值成功
        no_Check_list.add("sms.onLineRecharge.createIpnMessage");

        // E2E计算器:验货仓发货仓的查询
        no_Check_list.add("ups.warehouse.queryWarehouseByType");
        // E2E计算器币种的查询
        no_Check_list.add("ups.conversion.conversionConfig");
        // E2E计算器:国家的查询
        no_Check_list.add("ups.addressdata.allCountry");
        // E2E计算器:头程费用计算
        no_Check_list.add("e2e.firstfee.calculateFee");
        // E2E计算器:尾程费用计算
        no_Check_list.add("e2e.lastfee.calculateFee");
        // DOI计算费用
        no_Check_list.add("e2e.doifee.calculateFee");

        no_Check_list.add("CustomerController.fuzzyQueryCustomerByName");
        no_Check_list.add("CustomerController.checkCustomerCode");

        no_Check_list.add("winit.oauth.tools.api.verification");

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object obj,
                                Exception exception) throws Exception {

    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object obj,
                           ModelAndView modelandview) throws Exception {
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception {

        RequestMsg requestMsg = buildRequestMsg(request);
        long apiSeq = System.nanoTime();
        logger.info("Seq:" + apiSeq + BLANK_WHITE + "User:" + requestMsg.getApp_key() + BLANK_WHITE + "API:"
                    + requestMsg.getAction() + BLANK_WHITE + "start...");
        // API监控初始化
        Tracer.beginTrace(SpanCategory.URL.name(), requestMsg.getAction(), null, null, null);
        // set requestMsg
        RequestUtil.setRequestMsg(request, requestMsg);

        apiRequestHandlerHolder.doRequestDataCheck(request);

        CommandContext ctx = CommandContext.getContext();

        // 将请求Sequence和API调用开始时间放入上下文
        ctx.set(ApiConstant.API_REQUEST_SEQ, apiSeq);

        LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);

        // set Locale
        if (requestMsg.getLanguage() != null && requestMsg.getLanguage().equalsIgnoreCase("zh_CN")) {
            ctx.setLocale(Locale.SIMPLIFIED_CHINESE);
            localeResolver.setLocale(request, response, Locale.SIMPLIFIED_CHINESE);
        } else if (requestMsg.getLanguage() != null && requestMsg.getLanguage().equalsIgnoreCase("zh_TW")) {
            ctx.setLocale(Locale.TRADITIONAL_CHINESE);
            localeResolver.setLocale(request, response, Locale.TRADITIONAL_CHINESE);
        } else {
            ctx.setLocale(Locale.US);
            localeResolver.setLocale(request, response, Locale.US);
        }
        // set source
        if (StringUtils.isNotBlank(requestMsg.getPlatform())) {
            ctx.set(ApiConstant.API_SOURCE, requestMsg.getPlatform());
        } else {
            ctx.set(ApiConstant.API_SOURCE, ApiConstant.API_SOURCE_DEFAULT);
        }

        // 加入API监控
        handleTrace(requestMsg, request);

        return true;

    }

    /**
     * 从request里面构建RequestMsg参数
     * 
     * @param request
     * @return
     */
    private RequestMsg buildRequestMsg(HttpServletRequest request) {
        RequestMsg requestMsg = null;
        CommandContext ctx = CommandContext.getContext();
        String uri = ctx.getRequestUrl();
        if (StringUtils.isNotBlank(uri) && uri.contains(ApiConstant.AD_3E_CONTENT_PATH)) {
            requestMsg = RequestMsg3EUtil.doPrepare(request);
        } else {

            requestMsg = doPrepare(request);
        }
        return requestMsg;
    }

    /**
     * 参数处理
     * 
     * @param request
     * @return
     * @throws IOException
     */
    private RequestMsg doPrepare(ServletRequest request) {
        JSONObject json = null;
        RequestMsg requestMsg = null;
        String inputJsonStr = null;
        String encoding = request.getCharacterEncoding();

        try {
            if (StringUtils.isBlank(encoding)) {
                encoding = ApiConstant.CHARSET_UTF8;
            }
            inputJsonStr = IOUtils.toString(request.getInputStream(), encoding);

            request.setAttribute(ApiConstant.SOURCE_INPUT_STRING, inputJsonStr);

            if (StringUtils.isBlank(inputJsonStr)) {
                // 如果InputStream为空，则去request中取，如果仍然为空，则抛出异常
                inputJsonStr = (String) request.getAttribute(ApiConstant.SOURCE_INPUT_STRING);
                if (StringUtils.isBlank(inputJsonStr)) {
                    logger.warn("读取request请求参数失败,ContentType:" + request.getContentType() + " Encoding:"
                                + request.getCharacterEncoding());
                    throw new ApiException(ErrorCode.ILLEGAL_JSON_STRING);
                }
            }

            // 替换 '\' 为 '\\'
            // 修复#Bug 10284
            // inputJsonStr = inputJsonStr.replaceAll("\\\\", "\\\\\\\\");

            json = JSONObject.parseObject(inputJsonStr);
            // if(logger.isDebugEnabled()){
            // logger.debug("接收参数报文:"+json);
            // }
            String flag = apiSysConfigUtil.getConfigValueByKey("API_CHECK_INPUT_FLAG");
            if (StringUtils.isNotEmpty(flag) && "true".equalsIgnoreCase(flag)) {
                json = preHandleInputJson(json, inputJsonStr);
            }
            requestMsg = JSONObject.toJavaObject(json, RequestMsg.class);
            // 添加输出参数适配 ,只有API版本是2.0才生效，不会影响现有API add by 2016-07-25
            String version = requestMsg.getVersion();
            if (ApiConstant.API_VERSION_2.equals(version)) {
                requestMsg = ParameterAdapterUtils.handleInputAdapte(requestMsg);
            }

        } catch (JSONException e) {
            logger.warn("illegal json string:" + inputJsonStr);
            throw new ApiException(ErrorCode.ILLEGAL_JSON_STRING);
        } catch (IOException e) {
            logger.warn("charset not suport:" + encoding);
            throw new ApiException(ErrorCode.CHARSET_NOT_SUPORT);
        }
        return requestMsg;
    }


    private void handleTrace(RequestMsg requestMsg, HttpServletRequest request) {

        String app_key = requestMsg.getApp_key();
        if (StringUtils.isBlank(app_key) && requestMsg.getAction().contains("getToken")) {
            JSONObject json = JSONObject.parseObject(requestMsg.getData().toString());
            if (json.containsKey("userName")) {
                app_key = json.getString("userName");
            }
        }
        CommandContext ctx = CommandContext.getContext();
        ctx.set(ApiConstant.API_APP_KEY, app_key);
        // 用户信息
        Tracer.addData("user", app_key);

        String action = requestMsg.getAction();
        // 接口信息
        Tracer.addData("apiName", action);
        // 版本信息
        Tracer.addData("version", requestMsg.getVersion());

        String platform = requestMsg.getPlatform();
        if (StringUtils.isBlank(platform)) {
            // 未指定platform的特殊标识出来
            platform = ApiConstant.API_SOURCE_NONE;
        }
        // 平台信息
        Tracer.addData("source", platform);

        // 原始IP
        Tracer.addData("sourceIP", getIpAddress(request));

    }

    /**
     * 获取用户真实IP地址，不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址, 参考文章：
     * http://developer.51cto.com/art/201111/305181.htm
     * 可是，如果通过了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP值，究竟哪个才是真正的用户端的真实IP呢？
     * 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。 如：X-Forwarded-For：192.168.1.110,
     * 192.168.1.120, 192.168.1.130, 192.168.1.100 用户真实IP为： 192.168.1.110
     * 
     * @param request
     * @return
     */
    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (StringUtils.isNotBlank(ip) && ip.contains(",")) {
            ip = ip.substring(0, ip.indexOf(","));
        }
        // request.setAttribute(ApiConstant.SOURCE_IP, ip);
        CommandContext ctx = CommandContext.getContext();
        ctx.set(ApiConstant.SOURCE_IP, ip);
        return ip;
    }

    public static List<String> getNo_Check_list() {
        return no_Check_list;
    }

    /**
     * 预处理客户输入报文，将\替换为\\
     */
    private JSONObject preHandleInputJson(JSONObject json, String input) {
        String actionStr = apiSysConfigUtil.getConfigValueByKey("API_CHECK_INPUT_ACTION_LIST");
        if (StringUtils.isBlank(actionStr)) {
            return json;
        }
        String action = json.getString("action");
        String testRegex = apiSysConfigUtil.getConfigValueByKey("API_TEST_INPUT_REGEX");
        if (StringUtils.isBlank(testRegex)) {
            testRegex = "([^\\\\])(\\\\)(\\d)";
        }
        String[] actionArray = actionStr.split("\\,");
        Pattern pattern = Pattern.compile(testRegex);
        Matcher matcher = pattern.matcher(input);
        for (String string : actionArray) {
            if (action.equals(string) && matcher.find()) {
                String regex = apiSysConfigUtil.getConfigValueByKey("API_CHECK_INPUT_REGEX");
                String replacement = apiSysConfigUtil.getConfigValueByKey("API_CHECK_INPUT_REPLACEMENT");
                logger.info("开始替换报文：" + input);
                String result = input.replaceAll(regex, replacement);
                logger.info("替换结果：" + result);
                JSONObject jsonResult = JSONObject.parseObject(result);
                return jsonResult;
            }
        }
        return json;
    }
}
